#include "RssSocket.h"
#include "../util/AppData.h"

RssTcpSocket::RssTcpSocket()
{
	WSADATA wsaData;
	m_errorCode = 0;
	s = INVALID_SOCKET;

	m_socketState = NONE;
	m_bWSAStarted = false;

	if(!m_bWSAStarted)
	{
		m_errorCode = WSAStartup(MAKEWORD(2,2), &wsaData);
		if(m_errorCode == 0)
			m_bWSAStarted = true;
	}
}
bool RssTcpSocket::Initial() 
{
	this->OnStart();
	bool dontLinger = true;
	LINGER lingerOpt;

	if(!m_bWSAStarted)
		return false;

	//Exit if the socket has been already created
	if(s != INVALID_SOCKET)
	{
		m_errorCode = WSAEACCES;
		return false;
	}

	s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	
	if(s == INVALID_SOCKET)
	{
		m_errorCode = WSAGetLastError();
		return false;
	}

	lingerOpt.l_onoff = 0;
	lingerOpt.l_linger = 0;

	if(setsockopt(s, SOL_SOCKET, SO_DONTLINGER, (char*) &dontLinger, sizeof(BOOL)) != 0)
	{
		m_errorCode = WSAGetLastError();
		closesocket(s);
		return false;
	}

	if(setsockopt(s, SOL_SOCKET, SO_LINGER, (char*) &lingerOpt, sizeof(LINGER)) != 0)
	{
		m_errorCode = WSAGetLastError();
		closesocket(s);
		return false;
	}

	m_socketState = CREATED;
	return true;
}
bool RssTcpSocket::Connect(string &addr, uint remotePort)
{
	char hostStr[257];
	int wHostLen;
	ulong hostByIP;
	HOSTENT *hostByName;

	if(!m_bWSAStarted)
		return false;

	//Make sure the socket was created
	if(s == INVALID_SOCKET)
	{
		m_errorCode = WSAENOTSOCK;
		return false;
	}

	//We cannot connect if the socket is already connected (TCP only)
	if(m_socketState > CREATED)
	{
		m_errorCode = WSAEISCONN;
		return false;
	}

	//We cannot connect if the socket is listening (UDP only)
	if(m_socketState > CONNECTED)
	{
		m_errorCode = WSAEISCONN;
		return false;
	}

	//Check port values
	if(remotePort > 65535)
	{
		m_errorCode = WSAEINVAL;
		return false;
	}


	this->OnDnsQuery();
	//Gets address string and convert it from unicode to multibyte
	wHostLen = addr.length();
	if(wHostLen > 256)
		wHostLen = 256;
	strcpy(hostStr, addr.c_str());
	
	//Builds destination address
	memset(&m_remoteAddress, 0, sizeof(SOCKADDR_IN));
	m_remoteAddress.sin_family = AF_INET;
	m_remoteAddress.sin_port = htons(remotePort);

	hostByIP = inet_addr(hostStr);
	if(hostByIP == INADDR_NONE)
	{
		hostByName = gethostbyname(hostStr);
		if(hostByName == NULL)
		{
			m_errorCode = WSAGetLastError();
			return false;
		}
		m_remoteAddress.sin_addr = *((IN_ADDR*)hostByName->h_addr_list[0]);
	}
	else
		m_remoteAddress.sin_addr.s_addr = hostByIP;
	
	//Connects if TCP.
	//UDP is connectionless. However we'll use m_remoteAddress for sending data.

	this->OnConnecting();
	if(connect(s, (SOCKADDR*) &m_remoteAddress, sizeof(SOCKADDR_IN)) != 0)
	{
		m_errorCode = WSAGetLastError();
		return FALSE;
	}
	m_socketState = CONNECTED;

	return true;
}
void RssTcpSocket::Close()
{
	if(!m_bWSAStarted || s == INVALID_SOCKET)
		return;

	//After a shutodown reading thread will self terminate
	m_socketState = DISCONNECTING;
	shutdown(s, SD_BOTH);
	closesocket(s);
	s = INVALID_SOCKET;
	m_socketState = NONE;
	m_errorCode = 0;
	m_revData.clear();
}
int RssTcpSocket::Send(const char* buf, int len)
{
	int dataPtr = 0;
	int sentBytes = 0;

	if(!m_bWSAStarted)
		return SOCKET_ERROR;

	//We cannot send data if the socket's not connected
	if(m_socketState < CONNECTED)
	{
		m_errorCode = WSAENOTCONN;
		return SOCKET_ERROR;
	}

	//TCP listening socket cannot send data
	if(m_socketState == ACCEPTING)
	{
		m_errorCode = WSAENOTCONN;
		return SOCKET_ERROR;
	}

	//Have we a valid buffer?
	if(!buf || len <= 0)
	{
		m_errorCode = WSAEFAULT;
		return SOCKET_ERROR;
	}

	this->OnWriting();

	while(len > 0)
	{
		sentBytes = send(s, &buf[dataPtr], len, 0);
		if(sentBytes == SOCKET_ERROR)
		{
			m_errorCode = WSAGetLastError();
			return SOCKET_ERROR;
		}
		dataPtr += sentBytes;
		len -= sentBytes;
	}

	return dataPtr;
}
int RssTcpSocket::Read()
{
	this->OnReading();
	m_revDataLength = 0;
	this->m_revData.clear();
	int bytesRead;
	int total = 0;
	Packet packet;
	while(true) 
	{
		bytesRead = recv(s, packet.data, BUFFER_SIZE, 0);
		packet.len = bytesRead;

		if(bytesRead == SOCKET_ERROR || bytesRead <= 0)
		{
			//m_errorCode = WSAGetLastError();			
			break;
		}
		total += bytesRead;
		this->m_revData.push_back(packet);
	} 
	m_revDataLength = total;
	this->OnEnd();
	return total;
}

string RssTcpSocket::GetData()
{
	char* buf = new char[this->m_revDataLength + 1];	
	char* pos = buf;
	for(list<Packet>::iterator it = this->m_revData.begin(); it != this->m_revData.end(); ++it)
	{
		memcpy(pos,it->data,it->len);
		pos += it->len;
	}
	*pos = 0;
	this->m_revDataLength = 0;
	this->m_revData.clear();
	return buf;
}

RssTcpSocket::~RssTcpSocket()
{
	this->Close();
	this->OnEnd();
}